import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Scanner;

public class Table {
	
	private Deck deck;
	private List<Player> players;
	private int activeP;
	private boolean allIn;
	private int tableMoney;
	private int entranceBet;
	
	public static void main(String[] args) {
		Scanner in = new Scanner(System.in);
		
		int playersNumber = -1;
		
		while(playersNumber < 2 || playersNumber > 4)
		{
			System.out.println("Number of players (including bots and people): ");
			playersNumber = in.nextInt();
		}
		
		int botsNumber = -1;
		while(botsNumber < 0 || botsNumber > playersNumber)
		{
			System.out.println("Number of bots: ");
			botsNumber = in.nextInt();
		}
		
		int entranceBet = -1;
		while(entranceBet < 0){
			System.out.println("Bet on entrance: ");
			entranceBet = in.nextInt();
		}
		
		int coinsNumber = -1;
		while(coinsNumber < entranceBet){
			System.out.println("Number of coins: ");
			coinsNumber = in.nextInt();
		}
		
		
		List<IPlayerStrategy> players = new LinkedList<IPlayerStrategy>();
		for(int i = 0; i < botsNumber; i++)
		{
			players.add(new BotStrategy());
		}
		
		for(int i = 0; i < playersNumber-botsNumber; i++)
		{
			players.add(new HumanStrategy());
		}
		
		
		Deck deck = new Deck();
		
		Table table = new Table(players, deck, coinsNumber, entranceBet);
		table.playGame();
		
		in.close();
	}
	
	public Table(List<IPlayerStrategy> playersStrategies, Deck deck, int startingCoins, int entranceBet) throws TableIncorrectNumberOfPlayersException {
		if(deck == null) {
			throw new TableException("The deck shouldn't be null");
		}
		
		if(playersStrategies == null) {
			throw new TableIncorrectNumberOfPlayersException("The list shouldn't be null");
		}
		
		if(playersStrategies.isEmpty()) {
			throw new TableIncorrectNumberOfPlayersException("The list of players is empty");
		}
			
		if(playersStrategies.size() < 2) {
			throw new TableIncorrectNumberOfPlayersException("Too few players");
		}
			
		players = new LinkedList<Player>();
		for(IPlayerStrategy strategy: playersStrategies)
		{
			players.add(new Player(strategy, "Player", deck, startingCoins));
		}
		
		this.deck = deck;
		this.entranceBet = entranceBet;
		activeP = playersStrategies.size();
	}
	
	/*
	 * Archives player
	 */
	public void removePlayer(Player player){
		player.setStatus(Player.Status.Archived);
		activeP--;
	}
	

	public void betting(){
		
		Iterator<Player> iterP = players.iterator();
		
		Player lastPlayerRaising = null;
		Player p = iterP.next();
		
		int bet = 0;
		
		while(lastPlayerRaising != p)
		{
			if(!iterP.hasNext())
			{
				iterP = players.iterator();
			}
			p = iterP.next();
			
			if(p.getStatus() != Player.Status.Active){
				continue;
			}
			
			int playersBet = p.sendBet();
			//all-in
			if(playersBet == p.getCoins()){
				if(!allIn)
				{
					lastPlayerRaising = p;
				}
				allIn = true;
				
			}
			//check:
			else if(bet == 0 && playersBet == 0 && !allIn){
				if(lastPlayerRaising == null)lastPlayerRaising = p;
			}
			// bet & raise & call:
			else if(playersBet >= bet && !allIn){
				if (playersBet > bet) {
					lastPlayerRaising = p;
				}
				bet = playersBet;
				
				p.takeCoins(playersBet);
				tableMoney += playersBet;
			}
			//fold
			else {
				p.setStatus(Player.Status.Inactive);	
			}
		}
		
		
	}
	
	/**
	 * Charges players with entrance fee
	 */
	public void entranceBet()
	{
		for(Player p: players)
		{
			if(p.getCoins() < entranceBet)
			{
				removePlayer(p);
				continue;
			}
			
			if(p.getStatus() == Player.Status.Active)
			{
				p.takeCoins(entranceBet);
				tableMoney += entranceBet;
			}
		}
	}
	
	/**
	 * Starts a game with actual players
	 */
	public void playGame()
	{
		while(activeP > 1)
		{
			playParty();
		}
	}
	
	/**
	 * Starts a party with a actual players
	 * @throws TablePlayerIncorrectImplementation
	 */
	
	public void playParty() throws TablePlayerIncorrectImplementation {
		deck.shuffle();
		tableMoney = 0;
		allIn = false;
		
		for(Player p: players)
		{
			if(p.getStatus() != Player.Status.Archived)p.setStatus(Player.Status.Active);
		}
		
		entranceBet();
		
		for(Player p: players)
		{
			if(p.getStatus() == Player.Status.Active)
			{
				p.fillHand();
			}
		}
		
		betting();
		
		for(Player p: players)
		{
			if(p.getStatus() == Player.Status.Active)
			{
				p.exchangeCards();
			}
		}
		
		List<Player> leftPlayers = new LinkedList<Player>();
		
		for(Player p: players)
		{
			if(p.getStatus() == Player.Status.Active)
			{
				leftPlayers.add(p);
			}
		}
		
		Collections.sort(leftPlayers, new ListComparator());
		Collections.reverse(leftPlayers);
		
		ListComparator comp = new ListComparator();
		
		Iterator<Player> iter = leftPlayers.iterator();
		
		Player lastPwv = leftPlayers.get(0);
		while(iter.hasNext()) {
			Player pwv = iter.next();
			if(comp.compare(pwv, lastPwv) != 0) {
				lastPwv = pwv;
				break;
			}
		}
		int winners = 0;
		System.out.println("Winning players: ");
		iter = leftPlayers.iterator();
		
		while(iter.hasNext())
		{
			Player pwv = iter.next();
			if(pwv == lastPwv)break;
			System.out.println(pwv.getName() + ", ");
			/*** Set number of winnings */
			winners++;
		}
		
		if(winners == 1){
			//zabrac rownowartosc kasy winnera od innych playerow
			if(allIn){
				int winnersBet = leftPlayers.get(0).getCoins();
				for(Player p: leftPlayers)
				{
					if(p.equals(leftPlayers.get(0)) || p.getStatus() != Player.Status.Active)continue;
					int loosersBet = p.getCoins();
					
					leftPlayers.get(0).giveCoins(Math.min(loosersBet, winnersBet));
					p.takeCoins(Math.min(loosersBet, winnersBet));
				}
			}
			leftPlayers.get(0).giveCoins(tableMoney);
			tableMoney = 0;
		}
	}
}
